package org.elsfs.auth.client;

/**
 * @author zeng
 * @since 0.0.1
 */
import static org.assertj.core.api.Assertions.assertThat;

import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import javax.crypto.spec.SecretKeySpec;
import org.elsfs.auth.entity.ResponseBody;
import org.elsfs.framework.generator.SnowFlake;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

/** client_secret_jwt 模式 */
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
class CLIENT_SECRET_JWT extends AbstractClientTypeAuthorizationServerTests {
  private final String CLIENT_ID = "test_client_secret_jwt";
  private final String CLIENT_SECRET = "secret";
  //     PasswordEncoderFactories.createDelegatingPasswordEncoder().encode(CLIENT_SECRET);
  private final String CLIENT_SECRET_ENCODE =
      "{scrypt}$2a$10$5GM8oLNHFyb3DcMb00EtJekFvMSArwakMRyqf7QraHHDPeunh2cDK";

  @Test
  @Override
  @Order(30)
  void exchange() {
    // addRegisteredClient();
    MultiValueMap<String, Object> requestMap = new LinkedMultiValueMap<>();
    requestMap.add(
        OAuth2ParameterNames.CLIENT_ASSERTION_TYPE,
        "urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
    requestMap.add(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);
    requestMap.add(OAuth2ParameterNames.SCOPE, "openid profile");

    requestMap.add(OAuth2ParameterNames.CLIENT_ASSERTION, createClientSecretJwtToken());

    requestMap.add(
        OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue());

    RequestEntity<MultiValueMap<String, Object>> request =
        RequestEntity.post(getUri())
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .accept(MediaType.APPLICATION_JSON)
            .body(requestMap);
    // 3.响应体
    ResponseEntity<ResponseBody> exchange = restTemplate.exchange(request, ResponseBody.class);
    ResponseBody body = exchange.getBody();
    assertThat(body).isNotNull();
    LOGGER.info("{}", body);
  }

  private void addRegisteredClient() {
    RegisteredClient registeredClient =
        RegisteredClient.withId(new SnowFlake(31).nextId() + "")
            .clientId(CLIENT_ID)
            .clientSecret(CLIENT_SECRET_ENCODE)
            .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
            .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)
            .scope(OidcScopes.OPENID)
            .scope(OidcScopes.PROFILE)
            .clientSettings(
                ClientSettings.builder()
                    .tokenEndpointAuthenticationSigningAlgorithm(
                        MacAlgorithm.HS256) // client_secret_jwt
                    // 需要
                    // private_key_jwt 需要
                    .build())
            .tokenSettings(
                TokenSettings.builder()
                    .accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED) // REFERENCE 不需要设置
                    // jwk SELF_CONTAINED
                    // 需要设置 jwk
                    .build())
            .build();
    registeredClientRepository.save(registeredClient);
  }

  protected String getUri() {
    return new StringBuffer("http://localhost:")
        .append(this.environment.getProperty("local.server.port", "8080"))
        .append(PATH)
        .toString();
  }

  private String createClientSecretJwtToken() {
    try {
      // algorithm 必须是 HMACSHA256
      SecretKeySpec secretKeySpec =
          new SecretKeySpec(CLIENT_SECRET_ENCODE.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
      JWSSigner signer = new MACSigner(secretKeySpec);
      // subject, issuer, audience, expirationTime 这四个参数是必须的
      // 服务器那边会校验
      JWTClaimsSet claimsSet =
          new JWTClaimsSet.Builder()
              //  // 主体：固定clientId
              .subject(CLIENT_ID)
              .jwtID(CLIENT_ID)
              // 发行者：固定clientId
              .issuer(CLIENT_ID)
              // 受众 必填 必须和 配置ISSUER一致 授权中心的地址
              .audience(authorizationServerSettings.getIssuer())
              // 过期时间
              .expirationTime(new Date(System.currentTimeMillis() + 60 * 60 * 24 * 365 * 10))
              .build();
      JWSHeader jwsHeader = new JWSHeader(JWSAlgorithm.HS256);
      SignedJWT signedJWT = new SignedJWT(jwsHeader, claimsSet);
      signedJWT.sign(signer);
      String token = signedJWT.serialize();
      LOGGER.info("{}", token);
      return token;
    } catch (Exception e) {
      throw new NullPointerException(e.getMessage());
    }
  }
}
